package abel.wlmr.model.type;

import abel.util.StdBcdNumber;
import abel.wlmr.model.WlmrException;
import io.netty.buffer.ByteBuf;

/**
 * B.5.12.2.5.20 ERC60： 水表用户安全报警记录
 * 事件记录的数据格式见表 B.205。
 * 表B.205 远传水表用户事件记录数据单元格式
 * 数据内容 数据格式 字节数
 * ERC=60 BIN 1
 * 长度 Le BIN 1
 * 发生时间：分时日月年 见附录 C.15 5
 * D15：起/止标志 D14～D12：备用 D11～D0： pn（测量点号 1～2048） BIN 2
 * 报警编码 BIN 1
 * 报警阈值 见附录 C.29 5
 * 表B.206 报警内容与编码
 * 报警编码 报警内容
 * 1 水表大流量使用时间过长报警
 * 2 水表回流报警
 * 3 水流过载
 * 4-255 备用
 * 
 * @author Abel
 *
 */
public class Erc60Body extends ErcBaseBody {

    private static final short FIXED_LENGTH = (short) (5 + 2 + 1 + 5);

    /**
     * 发生时间：分时日月年 见附录 C.15 5
     */
    private StdBcdNumber occurTime;

    private byte startStop;

    /**
     * D15：起/止标志 D14～D12：备用 D11～D0： pn（测量点号 1～2048） BIN 2
     */
    private short pn;

    /**
     * 表B.206 报警内容与编码
     * 报警编码 报警内容
     * 1 水表大流量使用时间过长报警
     * 2 水表回流报警
     * 3 水流过载
     * 4-255 备用
     */
    private MeterExceptionField meterException = new MeterExceptionField();

    /**
     * 报警阈值 见附录 C.29 5
     */
    private FlowPointC29Body<FlowUnitField> warnThreshold = new FlowPointC29Body<FlowUnitField>(new FlowUnitField());

    public Erc60Body(short guessedLen) {
        super(guessedLen, (short) 60, FIXED_LENGTH);
    }

    public Erc60Body() {
        this((short) -1);
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.ErcBaseBody#appendBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public ByteBuf appendBuf(ByteBuf buf) {
        if (occurTime == null || occurTime.length() != 5) {
            throw new IllegalArgumentException("occur time should be C.15 format");
        }

        if (pn < 1 || pn > 2048) {
            throw new IllegalArgumentException("pn should be [1,2048]");
        }

        if (startStop < 0 || startStop > 1) {
            throw new IllegalArgumentException("start stop flag should be 0/1");
        }

        // append erc & le.
        buf.writeByte(erc);
        buf.writeByte(le);

        buf.writeBytes(occurTime.toBytes());
        buf.writeShortLE(startStop << 15 | pn);
        buf.writeByte(meterException.getValue());

        warnThreshold.appendBuf(buf);

        return buf;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.ErcBaseBody#parseBuf(io.netty.buffer.ByteBuf)
     */
    @Override
    public int parseBuf(ByteBuf buf) throws WlmrException {
        if (checkBuffer(buf) < 0) {
            return -1;
        }

        buf.skipBytes(MIN_LENGTH);

        occurTime = new StdBcdNumber();
        occurTime.populateBytes(buf, 5);

        int flag = buf.readShortLE();
        pn = (short) (flag & 0x0FFF);
        startStop = (byte) ((flag >> 15) & 1);

        meterException = new MeterExceptionField();
        meterException.parseValue(buf.readByte());

        warnThreshold.parseBuf(buf);

        return MIN_LENGTH + FIXED_LENGTH;
    }

    /* (non-Javadoc)
     * @see abel.wlmr.model.AppUnitBody#getFinalLen()
     */
    @Override
    public short getFinalLen() {
        return MIN_LENGTH + FIXED_LENGTH;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return String.format("{occurTime:%s, startStop:%s, pn:%s, meterException:%s, warnThreshold:%s}", occurTime,
            startStop, pn, meterException, warnThreshold);
    }

    /**
     * 发生时间：分时日月年 见附录 C.15 5
     * 
     * @return the occurTime
     */
    public StdBcdNumber getOccurTime() {
        return occurTime;
    }

    /**
     * 发生时间：分时日月年 见附录 C.15 5
     * 
     * @param occurTime
     *            the occurTime to set
     */
    public void setOccurTime(StdBcdNumber occurTime) {
        if (occurTime == null || occurTime.length() != 5) {
            throw new IllegalArgumentException("occur time should be C.15 format");
        }

        // may throw IAE
        occurTime.toMeterC15Joda();

        this.occurTime = occurTime;
    }

    /**
     * @return the startStop
     */
    public byte getStartStop() {
        return startStop;
    }

    /**
     * @param startStop
     *            the startStop to set
     */
    public void setStartStop(byte startStop) {
        if (startStop < 0 || startStop > 1) {
            throw new IllegalArgumentException("start stop flag should be 0/1");
        }

        this.startStop = startStop;
    }

    /**
     * D15：起/止标志 D14～D12：备用 D11～D0： pn（测量点号 1～2048） BIN 2
     * 
     * @return the pn
     */
    public short getPn() {
        return pn;
    }

    /**
     * D15：起/止标志 D14～D12：备用 D11～D0： pn（测量点号 1～2048） BIN 2
     * 
     * @param pn
     *            the pn to set
     */
    public void setPn(short pn) {
        if (pn < 1 || pn > 2048) {
            throw new IllegalArgumentException("pn should be [1,2048]");
        }

        this.pn = pn;
    }

    /**
     * 表B.206 报警内容与编码
     * 报警编码 报警内容
     * 1 水表大流量使用时间过长报警
     * 2 水表回流报警
     * 3 水流过载
     * 4-255 备用
     * 
     * @return the meterException
     */
    public MeterExceptionField getMeterException() {
        return meterException;
    }

    /**
     * 表B.206 报警内容与编码
     * 报警编码 报警内容
     * 1 水表大流量使用时间过长报警
     * 2 水表回流报警
     * 3 水流过载
     * 4-255 备用
     * 
     * @param meterException
     *            the meterException to set
     */
    public void setMeterException(MeterExceptionField meterException) {
        if (meterException != null) {
            throw new IllegalArgumentException("meter exception can't be null");
        }

        this.meterException = meterException;
    }

    /**
     * 报警阈值 见附录 C.29 5
     * 
     * @return the warnThreshold
     */
    public FlowPointC29Body<FlowUnitField> getWarnThreshold() {
        return warnThreshold;
    }

    /**
     * 报警阈值 见附录 C.29 5
     * 
     * @param warnThreshold
     *            the warnThreshold to set
     */
    public void setWarnThreshold(FlowPointC29Body<FlowUnitField> warnThreshold) {
        if (warnThreshold != null) {
            throw new IllegalArgumentException("warnThreshold can't be null");
        }

        this.warnThreshold = warnThreshold;
    }

}
